﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using OpenLS.Drawing;
using OpenLS.Drawing.UI;

namespace OpenLS.Spreadsheet.UI.Internals
{
    internal partial class CellClusterTextElement : CellClusterElement
    {
        internal delegate void Action();
        // Using a DependencyProperty as the backing store for Quality.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty QualityProperty =
            DependencyProperty.Register("Quality", typeof(ClusterRenderingQuality), typeof(CellClusterTextElement),
                                        new FrameworkPropertyMetadata(ClusterRenderingQuality.Text,
                                                                      FrameworkPropertyMetadataOptions.AffectsRender,
                                                                      (d, e) =>
                                                                      {
                                                                          var t = (CellClusterTextElement)d;
                                                                          t.OnQualityChanged(e);
                                                                      }));

#if DEBUG_RENDER
        private static readonly Brush testBrush = new SolidColorBrush(Color.FromArgb(10, 0, 30, 0));
        private static readonly Brush testOverflowBrush = new SolidColorBrush(Color.FromArgb(20, 50, 0, 50));
#endif
        //\\ VisualCollection<CellTextElement> _elements;
        public CellTextRenderingInformation GetDisplayInfo(Cell cell)
        {
            return new CellTextRenderingInformation(new CellDisplayInfoKey(cell, SpreadsheetElement.GetColorMode(this)));
        }

        public CellClusterTextElement(ClusteredCellTextControl parent, CellClusterKey key, Worksheet worksheet)
            : base(parent, key, worksheet)
        {
            Contract.Requires(parent != null);
            if (worksheet.View != null && worksheet.View.ZoomScale <= 40)
                Quality = ClusterRenderingQuality.Line;
        }


        public ClusterRenderingQuality Quality
        {
            get { return (ClusterRenderingQuality)GetValue(QualityProperty); }
            set { SetValue(QualityProperty, value); }
        }

        private void OnQualityChanged(DependencyPropertyChangedEventArgs e)
        {
            var oldValue = (ClusterRenderingQuality)e.OldValue;
            var newValue = (ClusterRenderingQuality)e.NewValue;
            if (oldValue == newValue)
            {
                // Contract.Assert(false);
                return;
            }
            if (newValue == ClusterRenderingQuality.NoText)
            {
                Dispatcher.BeginInvoke(DispatcherPriority.Background,
                                       new Action(delegate
                                                      {
                                                          Quality = ClusterRenderingQuality.Line;
                                                          InvalidateSelf();
                                                      }));
            }
            if (newValue == ClusterRenderingQuality.Line)
            {
                Dispatcher.BeginInvoke(DispatcherPriority.Background,
                                       new Action(delegate
                                                      {
                                                          Quality = ClusterRenderingQuality.Text;
                                                          InvalidateSelf();
                                                      }));
            }
        }


        public bool IsRecycled
        {
            get { return (bool)GetValue(IsRecycledProperty); }
            set { SetValue(IsRecycledProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsRecycled.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsRecycledProperty =
            DependencyProperty.Register("IsRecycled", typeof(bool), typeof(CellClusterTextElement),
                                        new UIPropertyMetadata(false, (d, e) =>
                                                                          {
                                                                              var c = (CellClusterTextElement)d;
                                                                              c.OnRecycledChanged(e);
                                                                          }));

        private void OnRecycledChanged(DependencyPropertyChangedEventArgs e)
        {
            var recycled = (bool)e.NewValue;
            if (!recycled && RecycledAndDirty)
                InvalidateSelf();
        }


        public bool RecycledAndDirty
        {
            get { return (bool)GetValue(RecycledAndDirtyProperty); }
            set { SetValue(RecycledAndDirtyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for RecycledAndDirty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RecycledAndDirtyProperty =
            DependencyProperty.Register("RecycledAndDirty", typeof(bool), typeof(CellClusterTextElement),
                                        new UIPropertyMetadata(false));



        protected IEnumerable<Cell> GetRenderedCells()
        {
            EditedCell editedCell = _parent.EditedCell;
            Cell exceptCell;
            if (editedCell != null)
                exceptCell = editedCell.Cell;
            else
            {
                exceptCell = null;
            }
            return Enumerable.Where(base.GetCells(), c => c != exceptCell);
        }


       /* protected override Size MeasureOverride(Size availableSize)
        {
            var p1 = _parent.GetAbsoluteLocation(new CellAddress(Key.Row, Key.Column));
            var p2 = _parent.GetAbsoluteLocation(new CellAddress(Key.Row + CellClusterKey.Size, Key.Column + CellClusterKey.Size));
            var result = base.MeasureOverride(availableSize);
            result = new Size(p2.X - p1.X, p2.Y - p1.Y);
            return result;
        }
        protected override Size ArrangeOverride(Size finalSize)
        {
            var result =  base.ArrangeOverride(finalSize);
            return result;
        }*/
#if DEBUG_RENDER
        private int counter = 0;
#endif
        protected override void OnRender(DrawingContext drawingContext)
        {
            Overflows = false;
#if DEBUG_RENDER
            drawingContext.DrawRectangle(null, new Pen(Brushes.LightGray, 1),
                                         new Rect(0, 0, ActualWidth, ActualHeight));
#endif
            if (IsRecycled)
            {
                RecycledAndDirty = true;
#if DEBUG_RENDER
                drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(10, 255, 0, 0)), null,
                                             new Rect(0, 0, ActualWidth, ActualHeight));
#endif
                return;
            }
            RecycledAndDirty = false;
            switch (Quality)
            {
                case ClusterRenderingQuality.NoText:
                    RenderNoText(drawingContext);
                    break;
                //  case ClusterRenderingQuality.Text:
                case ClusterRenderingQuality.Line:
                    RenderAsLines(drawingContext);
                    break;
                case ClusterRenderingQuality.Text:
                    {
                        RenderAsText(drawingContext);
                    }
                    break;
                default:
                    throw new NotSupportedException();
            }
#if DEBUG_RENDER
            counter++;
            drawingContext.DrawText(new FormattedText(counter.ToString(), CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, new Typeface("Arial"), 12, Brushes.Black), new Point());
#endif
        }

        private void RenderAsText(DrawingContext drawingContext)
        {
#if DEBUG_RENDER
            drawingContext.DrawRectangle(testBrush, new Pen(Brushes.Red, 1), new Rect(0, 0, ActualWidth, ActualHeight));

#endif
            _parent.FlushDirtyOverflowRows();
            var manager = _parent.LayoutManager;
            Rect thisRect = new Rect(0, 0, ActualWidth, ActualHeight);
            Typeface tf = new Typeface("Arial");
            foreach (Cell cell in GetRenderedCells())
            {
                Rect rect = GetRect(cell);

                var renderRect = TempDrawingHelper.RenderCell(drawingContext, new CellArrangeInfo(rect, cell, manager, this.Column.Offset), GetDisplayInfo,
                    SpreadsheetElement.GetColorMode(this), GetRect);
                if (!Overflows && cell.CanOverflow && !thisRect.Contains(renderRect))
                {
                    Overflows = true;
                }
            }
#if DEBUG_RENDER
            if (this.Overflows)
                drawingContext.DrawRectangle(testOverflowBrush, new Pen(Brushes.Black, 2) { DashStyle = DashStyles.Dash }, new Rect(0, 0, ActualWidth, ActualHeight));

#endif
        }


        private void RenderAsLines(DrawingContext drawingContext)
        {
#if DEBUG_RENDER
            drawingContext.DrawRectangle(Brushes.Orange, null, new Rect(0, 0, ActualWidth, ActualHeight));
#endif
            double y = Row.Offset;
            double x = Column.Offset;
            var cache = new Cache<Pair<Brush, double>, Pen>(
                pair =>
                {
                    var p = new Pen(pair.First, pair.Second) { DashStyle = DashStyles.Dot };
                    p.Freeze();
                    return p;
                });
            foreach (Cell cell in GetRenderedCells())
            {
                object formattedValue = cell.GetRawFormattedValue();
                if (formattedValue == null)
                    continue;
                string s = formattedValue.ToString();
                if (string.IsNullOrEmpty(s))
                    continue;
                var rect = new Rect(cell.Column.Offset - x, cell.Row.Offset - y, cell.Column.ActualWidth,
                                    cell.Row.ActualHeight);
                Point p1;
                Point p2;
                switch (cell.ActualHorizontalAlignment)
                {
                    case HorizontalCellAlignment.Right:
                        p1 = new Point(rect.Right - 1 - s.Length * cell.FontSize / 1.6, (rect.Top + rect.Bottom) / 2);
                        p2 = new Point(rect.Right -1, (rect.Top + rect.Bottom) / 2);
                        break;
                    case HorizontalCellAlignment.Center:
                        p1 = new Point(rect.Left + rect.Right / 2 - s.Length * cell.FontSize / 1.6 / 2 , (rect.Top + rect.Bottom) / 2);
                        p2 = new Point(rect.Left + rect.Right / 2 + s.Length * cell.FontSize / 1.6 / 2, (rect.Top + rect.Bottom) / 2);
                        break;
                    default:
                        p1 = new Point(rect.Left + 1, (rect.Top + rect.Bottom) / 2);
                        p2 = new Point(rect.Left + s.Length * cell.FontSize / 1.6, (rect.Top + rect.Bottom) / 2);

                        break;
                }
                Pen pen = cache[
                        new Pair<Brush, double>(
                            CellTextRenderingInformation.GetForegroundBrush(cell, SpreadsheetElement.GetColorMode(this)),
                            cell.FontSize / 2)];
                drawingContext.DrawLine(pen, p1, p2);
            }
        }

        private void RenderNoText(DrawingContext drawingContext)
        {
#if DEBUG_RENDER
            drawingContext.DrawRectangle(Brushes.Yellow, null, new Rect(0, 0, ActualWidth, ActualHeight));
#endif
        }

        private Rect GetRect(Cell cell)
        {
            Rect result = _parent.GetAbsoluteRect(cell);
            result.Offset(-Column.Offset, -Row.Offset);
            return result;
        }


      


        private IEnumerable<Row> GetRows()
        {
            for (int i = Key.Row; i < Key.Row + CellClusterKey.Size; i++)
            {
                yield return Worksheet.Rows[i];
            }
        }

        private IEnumerable<Column> GetColumns()
        {
            for (int i = Key.Column; i < Key.Column + CellClusterKey.Size; i++)
            {
                yield return Worksheet.Columns[i];
            }
        }

        internal bool ContainsDimension(Dimension dimension)
        {
            if (dimension is Column)
            {
                Column column = (Column) dimension;
                return Key.Column <= column.Index && Key.Column + CellClusterKey.Size > column.Index;
            }
            Row row = (Row) dimension;
            return Key.Row <= row.Index && Key.Row + CellClusterKey.Size > row.Index;
        }
    }
}